/**
 *
 *  Utilities.cc
 *  An Tao
 *
 *  Copyright 2018, An Tao.  All rights reserved.
 *  https://github.com/an-tao/drogon
 *  Use of this source code is governed by a MIT license
 *  that can be found in the License file.
 *
 *  Drogon
 *
 */

#include <drogon/utils/Utilities.h>
#include <trantor/utils/Logger.h>
#ifdef USE_BROTLI
#include <brotli/decode.h>
#include <brotli/encode.h>
#endif
#ifdef _WIN32
#include <Rpc.h>
#include <direct.h>
#include <io.h>
#pragma comment(lib, "Rpcrt4.lib")
#else
//#include <uuid/uuid.h>
#endif
#include <zlib.h>
#include <iomanip>
#include <mutex>
#include <sstream>
#include <stack>
#include <string>
#include <thread>
#include <algorithm>
#include <cctype>
#include <cstdlib>
#include <stdio.h>
#include <string.h>
#ifndef _WIN32
#include <unistd.h>
#endif
#include <sys/stat.h>
#include <fcntl.h>
#include <stdarg.h>

namespace drogon {
    namespace utils {
        static const std::string base64Chars =
            "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
            "abcdefghijklmnopqrstuvwxyz"
            "0123456789+/";

        static const std::string urlBase64Chars =
            "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
            "abcdefghijklmnopqrstuvwxyz"
            "0123456789-_";
        class Base64CharMap {
          public:
            Base64CharMap() {
                char index = 0;
                for (int c = 'A'; c <= 'Z'; ++c) {
                    charMap_[c] = index++;
                }
                for (int c = 'a'; c <= 'z'; ++c) {
                    charMap_[c] = index++;
                }
                for (int c = '0'; c <= '9'; ++c) {
                    charMap_[c] = index++;
                }
                charMap_[static_cast<int>('+')] = charMap_[static_cast<int>('-')] =
                                                      index++;
                charMap_[static_cast<int>('/')] = charMap_[static_cast<int>('_')] =
                                                      index;
                charMap_[0] = 0xff;
            }
            char getIndex(const char c) const noexcept {
                return charMap_[static_cast<int>(c)];
            }

          private:
            char charMap_[256] {0};
        };
        const static Base64CharMap base64CharMap;

        static inline bool isBase64(unsigned char c) {
            if (isalnum(c))
                return true;
            switch (c) {
            case '+':
            case '/':
            case '-':
            case '_':
                return true;
            }
            return false;
        }

        bool isInteger(const std::string &str) {
            for (auto const &c : str) {
                if (c > '9' || c < '0')
                    return false;
            }
            return true;
        }

        std::string genRandomString(int length) {
            static const char char_space[] =
                "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
            static std::once_flag once;
            static const size_t len = strlen(char_space);
            static const int randMax = RAND_MAX - (RAND_MAX % len);
            std::call_once(once, []() {
                std::srand(static_cast<unsigned int>(time(nullptr)));
            });

            int i;
            std::string str;
            str.resize(length);

            for (i = 0; i < length; ++i) {
                int x = std::rand();
                while (x >= randMax) {
                    x = std::rand();
                }
                x = (x % len);
                str[i] = char_space[x];
            }

            return str;
        }

        std::vector<char> hexToBinaryVector(const char *ptr, size_t length) {
            assert(length % 2 == 0);
            std::vector<char> ret(length / 2, '\0');
            for (size_t i = 0; i < ret.size(); ++i) {
                auto p = i * 2;
                char c1 = ptr[p];
                if (c1 >= '0' && c1 <= '9') {
                    c1 -= '0';
                } else if (c1 >= 'a' && c1 <= 'f') {
                    c1 -= 'a';
                    c1 += 10;
                } else if (c1 >= 'A' && c1 <= 'F') {
                    c1 -= 'A';
                    c1 += 10;
                } else {
                    return std::vector<char>();
                }
                char c2 = ptr[p + 1];
                if (c2 >= '0' && c2 <= '9') {
                    c2 -= '0';
                } else if (c2 >= 'a' && c2 <= 'f') {
                    c2 -= 'a';
                    c2 += 10;
                } else if (c2 >= 'A' && c2 <= 'F') {
                    c2 -= 'A';
                    c2 += 10;
                } else {
                    return std::vector<char>();
                }
                ret[i] = c1 * 16 + c2;
            }
            return ret;
        }
        std::string hexToBinaryString(const char *ptr, size_t length) {
            assert(length % 2 == 0);
            std::string ret(length / 2, '\0');
            for (size_t i = 0; i < ret.length(); ++i) {
                auto p = i * 2;
                char c1 = ptr[p];
                if (c1 >= '0' && c1 <= '9') {
                    c1 -= '0';
                } else if (c1 >= 'a' && c1 <= 'f') {
                    c1 -= 'a';
                    c1 += 10;
                } else if (c1 >= 'A' && c1 <= 'F') {
                    c1 -= 'A';
                    c1 += 10;
                } else {
                    return "";
                }
                char c2 = ptr[p + 1];
                if (c2 >= '0' && c2 <= '9') {
                    c2 -= '0';
                } else if (c2 >= 'a' && c2 <= 'f') {
                    c2 -= 'a';
                    c2 += 10;
                } else if (c2 >= 'A' && c2 <= 'F') {
                    c2 -= 'A';
                    c2 += 10;
                } else {
                    return "";
                }
                ret[i] = c1 * 16 + c2;
            }
            return ret;
        }
#ifdef _WIN32
        char *strptime(const char *s, const char *f, struct tm *tm) {
            // std::get_time is defined such that its
            // format parameters are the exact same as strptime.
            std::istringstream input(s);
            input.imbue(std::locale(setlocale(LC_ALL, nullptr)));
            input >> std::get_time(tm, f);
            if (input.fail()) {
                return nullptr;
            }
            return (char *)(s + input.tellg());
        }
        time_t timegm(struct tm *tm) {
            struct tm my_tm;

            memcpy(&my_tm, tm, sizeof(struct tm));

            /* _mkgmtime() changes the value of the struct tm* you pass in, so
             * use a copy
             */
            return _mkgmtime(&my_tm);
        }
#endif
        std::string binaryStringToHex(const unsigned char *ptr, size_t length) {
            std::string idString;
            for (size_t i = 0; i < length; ++i) {
                int value = (ptr[i] & 0xf0) >> 4;
                if (value < 10) {
                    idString.append(1, char(value + 48));
                } else {
                    idString.append(1, char(value + 55));
                }

                value = (ptr[i] & 0x0f);
                if (value < 10) {
                    idString.append(1, char(value + 48));
                } else {
                    idString.append(1, char(value + 55));
                }
            }
            return idString;
        }

        std::set<std::string> splitStringToSet(const std::string &str,
                                               const std::string &separator) {
            std::set<std::string> ret;
            std::string::size_type pos1, pos2;
            pos2 = 0;
            pos1 = str.find(separator);
            while (pos1 != std::string::npos) {
                if (pos1 != 0) {
                    std::string item = str.substr(pos2, pos1 - pos2);
                    ret.insert(item);
                }
                pos2 = pos1 + separator.length();
                while (pos2 < str.length() &&
                        str.substr(pos2, separator.length()) == separator)
                    pos2 += separator.length();
                pos1 = str.find(separator, pos2);
            }
            if (pos2 < str.length())
                ret.insert(str.substr(pos2));
            return ret;
        }

//std::string getUuid()
//{
//#if USE_OSSP_UUID
//    uuid_t* uuid;
//    uuid_create(&uuid);
//    uuid_make(uuid, UUID_MAKE_V4);
//    char* str{ nullptr };
//    size_t len{ 0 };
//    uuid_export(uuid, UUID_FMT_BIN, &str, &len);
//    uuid_destroy(uuid);
//    std::string ret{ binaryStringToHex((const unsigned char*)str, len) };
//    free(str);
//    return ret;
//#elif defined __FreeBSD__
//    uuid_t* uuid = new uuid_t;
//    char* binstr = (char*)malloc(16);
//    uuidgen(uuid, 1);
//#if _BYTE_ORDER == _LITTLE_ENDIAN
//    uuid_enc_le(binstr, uuid);
//#else  /* _BYTE_ORDER != _LITTLE_ENDIAN */
//    uuid_enc_be(binstr, uuid);
//#endif /* _BYTE_ORDER == _LITTLE_ENDIAN */
//    delete uuid;
//    std::string ret{ binaryStringToHex((const unsigned char*)binstr, 16) };
//    free(binstr);
//    return ret;
//#elif defined _WIN32
//    uuid_t uu;
//    UuidCreate(&uu);
//    char tempStr[100];
//    auto len = snprintf(tempStr,
//        sizeof(tempStr),
//        "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
//        uu.Data1,
//        uu.Data2,
//        uu.Data3,
//        uu.Data4[0],
//        uu.Data4[1],
//        uu.Data4[2],
//        uu.Data4[3],
//        uu.Data4[4],
//        uu.Data4[5],
//        uu.Data4[6],
//        uu.Data4[7]);
//    return std::string{ tempStr, static_cast<size_t>(len) };
//#else
//    uuid_t uu;
//    uuid_generate(uu);
//    return binaryStringToHex(uu, 16);
//#endif
//
//}

        std::string base64Encode(const unsigned char *bytes_to_encode,
                                 unsigned int in_len,
                                 bool url_safe) {
            std::string ret;
            int i = 0;
            unsigned char char_array_3[3];
            unsigned char char_array_4[4];

            const std::string &charSet = url_safe ? urlBase64Chars : base64Chars;

            while (in_len--) {
                char_array_3[i++] = *(bytes_to_encode++);
                if (i == 3) {
                    char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
                    char_array_4[1] = ((char_array_3[0] & 0x03) << 4) +
                                      ((char_array_3[1] & 0xf0) >> 4);
                    char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) +
                                      ((char_array_3[2] & 0xc0) >> 6);
                    char_array_4[3] = char_array_3[2] & 0x3f;

                    for (i = 0; (i < 4); ++i)
                        ret += charSet[char_array_4[i]];
                    i = 0;
                }
            }

            if (i) {
                for (int j = i; j < 3; ++j)
                    char_array_3[j] = '\0';

                char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
                char_array_4[1] =
                    ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
                char_array_4[2] =
                    ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
                char_array_4[3] = char_array_3[2] & 0x3f;

                for (int j = 0; (j < i + 1); ++j)
                    ret += charSet[char_array_4[j]];

                while ((i++ < 3))
                    ret += '=';
            }
            return ret;
        }

        std::vector<char> base64DecodeToVector(const std::string &encoded_string) {
            auto in_len = encoded_string.size();
            int i = 0;
            int in_{0};
            char char_array_4[4], char_array_3[3];
            std::vector<char> ret;
            ret.reserve(in_len);

            while (in_len-- && (encoded_string[in_] != '=') &&
                    isBase64(encoded_string[in_])) {
                char_array_4[i++] = encoded_string[in_];
                ++in_;
                if (i == 4) {
                    for (i = 0; i < 4; ++i) {
                        char_array_4[i] = base64CharMap.getIndex(char_array_4[i]);
                    }

                    char_array_3[0] =
                        (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
                    char_array_3[1] = ((char_array_4[1] & 0xf) << 4) +
                                      ((char_array_4[2] & 0x3c) >> 2);
                    char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

                    for (i = 0; (i < 3); ++i)
                        ret.push_back(char_array_3[i]);
                    i = 0;
                }
            }

            if (i) {
                for (int j = i; j < 4; ++j)
                    char_array_4[j] = 0;

                for (int j = 0; j < 4; ++j) {
                    char_array_4[j] = base64CharMap.getIndex(char_array_4[j]);
                }

                char_array_3[0] =
                    (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
                char_array_3[1] =
                    ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
                char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

                for (int j = 0; (j < i - 1); ++j)
                    ret.push_back(char_array_3[j]);
            }

            return ret;
        }

        std::string base64Decode(const std::string &encoded_string) {
            auto in_len = encoded_string.size();
            int i = 0;
            int in_{0};
            unsigned char char_array_4[4], char_array_3[3];
            std::string ret;

            while (in_len-- && (encoded_string[in_] != '=') &&
                    isBase64(encoded_string[in_])) {
                char_array_4[i++] = encoded_string[in_];
                ++in_;
                if (i == 4) {
                    for (i = 0; i < 4; ++i) {
                        char_array_4[i] = base64CharMap.getIndex(char_array_4[i]);
                    }
                    char_array_3[0] =
                        (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
                    char_array_3[1] = ((char_array_4[1] & 0xf) << 4) +
                                      ((char_array_4[2] & 0x3c) >> 2);
                    char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

                    for (i = 0; (i < 3); ++i)
                        ret += char_array_3[i];
                    i = 0;
                }
            }

            if (i) {
                for (int j = i; j < 4; ++j)
                    char_array_4[j] = 0;

                for (int j = 0; j < 4; ++j) {
                    char_array_4[j] = base64CharMap.getIndex(char_array_4[j]);
                }

                char_array_3[0] =
                    (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
                char_array_3[1] =
                    ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
                char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

                for (int j = 0; (j < i - 1); ++j)
                    ret += char_array_3[j];
            }

            return ret;
        }
        static std::string charToHex(char c) {
            std::string result;
            char first, second;

            first = (c & 0xF0) / 16;
            first += first > 9 ? 'A' - 10 : '0';
            second = c & 0x0F;
            second += second > 9 ? 'A' - 10 : '0';

            result.append(1, first);
            result.append(1, second);

            return result;
        }
        std::string urlEncodeComponent(const std::string &src) {
            std::string result;
            std::string::const_iterator iter;

            for (iter = src.begin(); iter != src.end(); ++iter) {
                switch (*iter) {
                case ' ':
                    result.append(1, '+');
                    break;
                // alnum
                case 'A':
                case 'B':
                case 'C':
                case 'D':
                case 'E':
                case 'F':
                case 'G':
                case 'H':
                case 'I':
                case 'J':
                case 'K':
                case 'L':
                case 'M':
                case 'N':
                case 'O':
                case 'P':
                case 'Q':
                case 'R':
                case 'S':
                case 'T':
                case 'U':
                case 'V':
                case 'W':
                case 'X':
                case 'Y':
                case 'Z':
                case 'a':
                case 'b':
                case 'c':
                case 'd':
                case 'e':
                case 'f':
                case 'g':
                case 'h':
                case 'i':
                case 'j':
                case 'k':
                case 'l':
                case 'm':
                case 'n':
                case 'o':
                case 'p':
                case 'q':
                case 'r':
                case 's':
                case 't':
                case 'u':
                case 'v':
                case 'w':
                case 'x':
                case 'y':
                case 'z':
                case '0':
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                // mark
                case '-':
                case '_':
                case '.':
                case '!':
                case '~':
                case '*':
                case '(':
                case ')':
                    result.append(1, *iter);
                    break;
                // escape
                default:
                    result.append(1, '%');
                    result.append(charToHex(*iter));
                    break;
                }
            }

            return result;
        }
        std::string urlEncode(const std::string &src) {
            std::string result;
            std::string::const_iterator iter;

            for (iter = src.begin(); iter != src.end(); ++iter) {
                switch (*iter) {
                case ' ':
                    result.append(1, '+');
                    break;
                // alnum
                case 'A':
                case 'B':
                case 'C':
                case 'D':
                case 'E':
                case 'F':
                case 'G':
                case 'H':
                case 'I':
                case 'J':
                case 'K':
                case 'L':
                case 'M':
                case 'N':
                case 'O':
                case 'P':
                case 'Q':
                case 'R':
                case 'S':
                case 'T':
                case 'U':
                case 'V':
                case 'W':
                case 'X':
                case 'Y':
                case 'Z':
                case 'a':
                case 'b':
                case 'c':
                case 'd':
                case 'e':
                case 'f':
                case 'g':
                case 'h':
                case 'i':
                case 'j':
                case 'k':
                case 'l':
                case 'm':
                case 'n':
                case 'o':
                case 'p':
                case 'q':
                case 'r':
                case 's':
                case 't':
                case 'u':
                case 'v':
                case 'w':
                case 'x':
                case 'y':
                case 'z':
                case '0':
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                // mark
                case '-':
                case '_':
                case '.':
                case '!':
                case '~':
                case '*':
                case '\'':
                case '(':
                case ')':
                case '&':
                case '=':
                case '/':
                case '\\':
                case '?':
                    result.append(1, *iter);
                    break;
                // escape
                default:
                    result.append(1, '%');
                    result.append(charToHex(*iter));
                    break;
                }
            }

            return result;
        }
        bool needUrlDecoding(const char *begin, const char *end) {
            return std::find_if(begin, end, [](const char c) {
                return c == '+' || c == '%';
            }) != end;
        }
        std::string urlDecode(const char *begin, const char *end) {
            std::string result;
            size_t len = end - begin;
            result.reserve(len * 2);
            int hex = 0;
            for (size_t i = 0; i < len; ++i) {
                switch (begin[i]) {
                case '+':
                    result += ' ';
                    break;
                case '%':
                    if ((i + 2) < len && isxdigit(begin[i + 1]) &&
                            isxdigit(begin[i + 2])) {
                        unsigned int x1 = begin[i + 1];
                        if (x1 >= '0' && x1 <= '9') {
                            x1 -= '0';
                        } else if (x1 >= 'a' && x1 <= 'f') {
                            x1 = x1 - 'a' + 10;
                        } else if (x1 >= 'A' && x1 <= 'F') {
                            x1 = x1 - 'A' + 10;
                        }
                        unsigned int x2 = begin[i + 2];
                        if (x2 >= '0' && x2 <= '9') {
                            x2 -= '0';
                        } else if (x2 >= 'a' && x2 <= 'f') {
                            x2 = x2 - 'a' + 10;
                        } else if (x2 >= 'A' && x2 <= 'F') {
                            x2 = x2 - 'A' + 10;
                        }
                        hex = x1 * 16 + x2;

                        result += char(hex);
                        i += 2;
                    } else {
                        result += '%';
                    }
                    break;
                default:
                    result += begin[i];
                    break;
                }
            }
            return result;
        }

        /* Compress gzip data */
        std::string gzipCompress(const char *data, const size_t ndata) {
            z_stream strm = {0};
            if (data && ndata > 0) {
                if (deflateInit2(&strm,
                                 Z_DEFAULT_COMPRESSION,
                                 Z_DEFLATED,
                                 MAX_WBITS + 16,
                                 8,
                                 Z_DEFAULT_STRATEGY) != Z_OK) {
                    LOG_ERROR << "deflateInit2 error!";
                    return std::string{};
                }
                std::string outstr;
                outstr.resize(compressBound(static_cast<uLong>(ndata)));
                strm.next_in = (Bytef *)data;
                strm.avail_in = static_cast<uInt>(ndata);
                int ret;
                do {
                    if (strm.total_out >= outstr.size()) {
                        outstr.resize(strm.total_out * 2);
                    }
                    assert(outstr.size() >= strm.total_out);
                    strm.avail_out = static_cast<uInt>(outstr.size() - strm.total_out);
                    strm.next_out = (Bytef *)outstr.data() + strm.total_out;
                    ret = deflate(&strm, Z_FINISH); /* no bad return value */
                    if (ret == Z_STREAM_ERROR) {
                        (void)deflateEnd(&strm);
                        return std::string{};
                    }
                } while (strm.avail_out == 0);
                assert(strm.avail_in == 0);
                assert(ret == Z_STREAM_END); /* stream will be complete */
                outstr.resize(strm.total_out);
                /* clean up and return */
                (void)deflateEnd(&strm);
                return outstr;
            }
            return std::string{};
        }

        std::string gzipDecompress(const char *data, const size_t ndata) {
            if (ndata == 0)
                return std::string(data, ndata);

            auto full_length = ndata;

            auto decompressed = std::string(full_length * 2, 0);
            bool done = false;

            z_stream strm = {0};
            strm.next_in = (Bytef *)data;
            strm.avail_in = static_cast<uInt>(ndata);
            strm.total_out = 0;
            strm.zalloc = Z_NULL;
            strm.zfree = Z_NULL;
            if (inflateInit2(&strm, (15 + 32)) != Z_OK) {
                LOG_ERROR << "inflateInit2 error!";
                return std::string{};
            }
            while (!done) {
                // Make sure we have enough room and reset the lengths.
                if (strm.total_out >= decompressed.length()) {
                    decompressed.resize(decompressed.length() * 2);
                }
                strm.next_out = (Bytef *)decompressed.data() + strm.total_out;
                strm.avail_out =
                    static_cast<uInt>(decompressed.length() - strm.total_out);
                // Inflate another chunk.
                int status = inflate(&strm, Z_SYNC_FLUSH);
                if (status == Z_STREAM_END) {
                    done = true;
                } else if (status != Z_OK) {
                    break;
                }
            }
            if (inflateEnd(&strm) != Z_OK)
                return std::string{};
            // Set real length.
            if (done) {
                decompressed.resize(strm.total_out);
                return decompressed;
            } else {
                return std::string{};
            }
        }

        char *getHttpFullDate(const trantor::Date &date) {
            static thread_local int64_t lastSecond = 0;
            static thread_local char lastTimeString[128] = {0};
            auto nowSecond = date.microSecondsSinceEpoch() / MICRO_SECONDS_PRE_SEC;
            if (nowSecond == lastSecond) {
                return lastTimeString;
            }
            lastSecond = nowSecond;
            date.toCustomedFormattedString("%a, %d %b %Y %H:%M:%S GMT",
                                           lastTimeString,
                                           sizeof(lastTimeString));
            return lastTimeString;
        }
        trantor::Date getHttpDate(const std::string &httpFullDateString) {
            struct tm tmptm;
            strptime(httpFullDateString.c_str(), "%a, %d %b %Y %H:%M:%S", &tmptm);
            auto epoch = timegm(&tmptm);
            return trantor::Date(epoch * MICRO_SECONDS_PRE_SEC);
        }
        std::string formattedString(const char *format, ...) {
            std::string strBuffer(128, 0);
            va_list ap, backup_ap;
            va_start(ap, format);
            va_copy(backup_ap, ap);
            auto result = vsnprintf((char *)strBuffer.data(),
                                    strBuffer.size(),
                                    format,
                                    backup_ap);
            va_end(backup_ap);
            if ((result >= 0) && ((std::string::size_type)result < strBuffer.size())) {
                strBuffer.resize(result);
            } else {
                while (true) {
                    if (result < 0) {
                        // Older snprintf() behavior. Just try doubling the buffer size
                        strBuffer.resize(strBuffer.size() * 2);
                    } else {
                        strBuffer.resize(result + 1);
                    }

                    va_copy(backup_ap, ap);
                    auto result = vsnprintf((char *)strBuffer.data(),
                                            strBuffer.size(),
                                            format,
                                            backup_ap);
                    va_end(backup_ap);

                    if ((result >= 0) &&
                            ((std::string::size_type)result < strBuffer.size())) {
                        strBuffer.resize(result);
                        break;
                    }
                }
            }
            va_end(ap);
            return strBuffer;
        }

        int createPath(const std::string &path) {
            auto tmpPath = path;
            std::stack<std::string> pathStack;
#ifdef _WIN32
            while (_access(tmpPath.c_str(), 06) != 0)
#else
            while (access(tmpPath.c_str(), F_OK) != 0)
#endif
            {
                if (tmpPath == "./" || tmpPath == "/")
                    return -1;
                while (tmpPath[tmpPath.length() - 1] == '/')
                    tmpPath.resize(tmpPath.length() - 1);
                auto pos = tmpPath.rfind('/');
                if (pos != std::string::npos) {
                    pathStack.push(tmpPath.substr(pos));
                    tmpPath = tmpPath.substr(0, pos + 1);
                } else {
                    pathStack.push(tmpPath);
                    tmpPath.clear();
                    break;
                }
            }
            while (pathStack.size() > 0) {
                if (tmpPath.empty()) {
                    tmpPath = pathStack.top();
                } else {
                    if (tmpPath[tmpPath.length() - 1] == '/') {
                        tmpPath.append(pathStack.top());
                    } else {
                        tmpPath.append("/").append(pathStack.top());
                    }
                }
                pathStack.pop();

#ifdef _WIN32
                if (_mkdir(tmpPath.c_str()) == -1)
#else
                if (mkdir(tmpPath.c_str(), 0755) == -1)
#endif
                {
                    LOG_ERROR << "Can't create path:" << path;
                    return -1;
                }
            }
            return 0;
        }
#ifdef USE_BROTLI
        std::string brotliCompress(const char *data, const size_t ndata) {
            std::string ret;
            if (ndata == 0)
                return ret;
            ret.resize(BrotliEncoderMaxCompressedSize(ndata));
            size_t encodedSize{ret.size()};
            auto r = BrotliEncoderCompress(5,
                                           BROTLI_DEFAULT_WINDOW,
                                           BROTLI_DEFAULT_MODE,
                                           ndata,
                                           (const uint8_t *)(data),
                                           &encodedSize,
                                           (uint8_t *)(ret.data()));
            if (r == BROTLI_FALSE)
                ret.resize(0);
            else
                ret.resize(encodedSize);
            return ret;
        }
        std::string brotliDecompress(const char *data, const size_t ndata) {
            if (ndata == 0)
                return std::string(data, ndata);

            size_t availableIn = ndata;
            auto nextIn = (const uint8_t *)(data);
            auto decompressed = std::string(availableIn * 3, 0);
            size_t availableOut = decompressed.size();
            auto nextOut = (uint8_t *)(decompressed.data());
            size_t totalOut{0};
            bool done = false;
            auto s = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr);
            while (!done) {
                auto result = BrotliDecoderDecompressStream(
                                  s, &availableIn, &nextIn, &availableOut, &nextOut, &totalOut);
                if (result == BROTLI_DECODER_RESULT_SUCCESS) {
                    decompressed.resize(totalOut);
                    done = true;
                } else if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
                    assert(totalOut == decompressed.size());
                    decompressed.resize(totalOut * 2);
                    nextOut = (uint8_t *)(decompressed.data() + totalOut);
                    availableOut = totalOut;
                } else {
                    decompressed.resize(0);
                    done = true;
                }
            }
            BrotliDecoderDestroyInstance(s);
            return decompressed;
        }
#else
        std::string brotliCompress(const char *data, const size_t ndata) {
            LOG_ERROR << "If you do not have the brotli package installed, you cannot "
                      "use brotliCompress()";
            abort();
        }
        std::string brotliDecompress(const char *data, const size_t ndata) {
            LOG_ERROR << "If you do not have the brotli package installed, you cannot "
                      "use brotliDecompress()";
            abort();
        }
#endif

    }  // namespace utils
}  // namespace drogon
